package com.patterns.singleton;

/**
 * 懒汉式单例模式在第一次调用的时候进行实例化
 * @author fwj
 * @date 2020/12/8 14:36
 **/
public class LazySingleton {
    private static LazySingleton instance = null;

    private LazySingleton() {
    }

    /**
     * 1、适用于单线程环境（不推荐）
     * 此方式在单线程的时候工作正常，但在多线程的情况下就有问题了。如果两个线程同时运行到判断instance是否为null的if语句，
     * 并且instance的确没有被创建时，那么两个线程都会创建一个实例，此时类型Singleton1就不再满足单例模式的要求了。
     */
    public static LazySingleton getInstanceA() {
        if (null == instance) {
            instance = new LazySingleton();
        }
        return instance;
    }

    /**
     * 2、适用于多线程环境，但效率不高（不推荐），可能对象还有其他业务逻辑
     * 为了保证在多线程环境下我们还是只能得到该类的一个实例，只需要在getInstanceB()方法加上同步关键字sychronized，就可以了。
     * 但每次调用getInstanceB()方法时都被synchronized关键字锁住了，会引起线程阻塞，影响程序的性能。
     */
    public static synchronized LazySingleton getInstanceB() {
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }

    /**
     * 3、双重检查加锁（推荐） Double Check Lock(DCL)
     * 为了在多线程环境下，不影响程序的性能，不让线程每次调用getInstanceC()方法时都加锁，而只是在实例未被创建时再加锁，在加锁处理里面还需要判断一次实例是否已存在。
     *
     * DCL还是会存在问题，在new了一半的时候会出现指令重排序出现了半初始化状态，第二个线程使用了半初始化的对象；【饿汉式不存在这个问题，jvm保证只有一个】
     *
     * 这种方式要在instance 前加上volatitle修饰符，不加会出现指令重排的问题
     */
    public static LazySingleton getInstanceC() {
        // 先判断实例是否存在，若不存在再对类对象进行加锁处理，提高性能：如果不加，多个线程就会锁多次
        if (instance == null) {
            synchronized (LazySingleton.class) {
                if (instance == null) { //是否有其他线程打断
                    instance = new LazySingleton();
                }
            }
        }
        return instance;
    }

}
